#include "read_options.h"
namespace po = boost::program_options;
using std::vector;
using std::string;

#include <boost/format.hpp>
		using boost::format;
#include <boost/foreach.hpp>
#include <boost/unordered_set.hpp>
	using boost::unordered_set;
#include <boost/regex.hpp>
	using boost::regex;
	using boost::regex_match;
	using boost::smatch;
#include <tuple>

bool read_options(prog_options& opt, int argc, char *argv[])
{
	opt.mandatory.add_options()
								("input_file,i",po::value<string>(&opt.input_file_name)
										//->set_name("FILE"),
										,"SNPs in tab delimited format: <contig><pos><category>. "
										 "The last column is optional.\n"
										 "See --intervals_only")
								("output_file,o",po::value<string>(&opt.output_file_name)
										//->set_name("FILE"),
										,"Matching SNPs in tab delimited format: <contig><pos>[<category>][<interval>]. "
										 "The last column is optional. See --add_interval_to_output.\n"
										 "Patterns may be used to send SNPS of different categories or interval types to "
										 "different files:\n"
										 "'/here/snps.[INTERVAL_TYPE].[CATEGORY].data' would create "
										 "files such as:\n"
										 "'/here/snps.coding.qtl_interval_112.data' etc. given a category of "
										 "'qtl_interval_112' and interval type of 'coding'.")
								("interval_file,I",po::value<vector<string> >(&opt.interval_file_names)
										//->set_name("FILE"),
										,"In tab delimited format: <contig><start><end>. ")
								("interval_type,T",po::value<vector<string> >(&opt.interval_type_names)
										//->set_name("FILE"),
										,"Corresponding type for each interval file. ")
								("default_type,D",po::value<string>(&opt.default_interval_type_name)
										//->set_name("FILE"),
										,"Type of SNPs outside all intervals.")
								("contig_sizes_file,C",po::value<string>(&opt.contig_sizes_file_name)
										//->set_name("FILE"),
										,"In tab delimited format <contig><beg><end>.");


	vector<string> to_save_interval_type_name_category_pairs;
	opt.optional.add_options()
								("category_saved,c",po::value<vector<string> >(&opt.to_save_categories)
										//->set_name("FILE"),
										,"Save SNPs with these categories.")
								("interval_saved,t",po::value<vector<string> >(&opt.to_save_interval_type_names)
										//->set_name("FILE"),
										,"Save SNPs from these intervals.")
								("category_interval_saved,p", po::value<vector<string> >(&to_save_interval_type_name_category_pairs)
										//->set_name("FILE"),
										,"Save SNPs from these categories if they are also "
										 "in these intervals. Should be specified as "
										 "'CATEGORY[&&]INTERVAL_NAME', i.e. double ampersands "
										 "in square brackets between the category and interval names. "
										 "N.B. Remember to quote parameters on the command line.")
								("add_interval_to_output,a",po::value<bool>(&opt.add_interval_to_output)
										->zero_tokens()
										,"Add interval type as an extra column in the output.")
								("jobs,j",po::value<unsigned>(&opt.jobs)
										->default_value(20)
										,"Allow N jobs to run in parallel at the same time.")
								("zero_based_snps,Z",po::value<bool>(&opt.zero_based_snps)
										->zero_tokens()
										,"SNP positions specified in zero based [) coordinates (UCSC) rather than 1-based [] (Ensembl).")
								("zero_based_intervals,z",po::value<bool>(&opt.zero_based_intervals)
										->zero_tokens()
										,"Intervals specified in zero based [) coordinates (UCSC) rather than 1-based [] (Ensembl).");
								


	
	opt.config_file.add_options()
			("input_file,"							  "i",po::value<string>				(&opt.input_file_name						))  
			("output_file,"							  "o",po::value<string>				(&opt.output_file_name						))  
			("interval_file,"						  "I",po::value<vector<string> >	(&opt.interval_file_names					))  
			("interval_type,"						  "T",po::value<vector<string> >	(&opt.interval_type_names					))  
			("default_type,"						  "D",po::value<string>				(&opt.default_interval_type_name			))  
			("contig_sizes_file,"					  "C",po::value<string>				(&opt.contig_sizes_file_name				))  
			("category_to_save,"					  "c",po::value<vector<string> >	(&opt.to_save_categories					))  
			("interval_type_to_save,"				  "t",po::value<vector<string> >	(&opt.to_save_interval_type_names			))  
			("category_interval_type_pairs_to_save,"  "p",po::value<vector<string> >	(&to_save_interval_type_name_category_pairs	))  
			("add_interval_to_output,"				  "a",po::value<bool>				(&opt.add_interval_to_output				))  
			("jobs,"								  "j",po::value<unsigned>			(&opt.jobs									))  
			("zero_based_snps,"						  "Z",po::value<bool>				(&opt.zero_based_snps						))  
			("zero_based_intervals,"				  "z",po::value<bool>				(&opt.zero_based_intervals					)); 



	if (!opt.process_command_line(argc, argv))
		return false;

	//
	//	make sure we are trying to save a real interval type 
	// 
	unordered_set<string> interval_type_names(opt.interval_type_names.begin(), opt.interval_type_names.end());
	BOOST_FOREACH(const string& n, opt.to_save_interval_type_names) 
	{
		if (interval_type_names.count(n) == 0)
			throw std::runtime_error((boost::format("Specified interval type [--interval_type_to_save %1$] is not "
													"in the list of interval types given by --interval_type.") 
													% n).str());
	}
	 
	//
	//	make sure interval type  / category names are well formed
	// 
	static boost::regex r(R"((.+)\[&&\](.+))");
	BOOST_FOREACH(const string& name_pair, to_save_interval_type_name_category_pairs) 
	{
		boost::smatch what;
		if (!regex_match(name_pair, what, r)) 
			throw std::runtime_error((boost::format("--category_interval_type_pairs_to_save %1$ was not specified in "
													"the 'CATEGORY[&&]INTERVAL_NAME' format.") 
													% name_pair).str());
		opt.to_save_interval_type_name_category_pairs.emplace_back(what[1], what[2]);

		if (interval_type_names.count(what[1]) == 0)
			throw std::runtime_error((boost::format("Specified interval type [--interval_type_to_save %1$] is not "
													"in the list of interval types given by --interval_type.") 
													% what[1]).str());

	}


	// check number of interval types and file names match
	if (opt.interval_type_names.size() != opt.interval_file_names.size())
		throw std::runtime_error((boost::format("The number of --interval_file (%1$)"
												"should match the number of "
												"--interval_type (%2$)") 
												% opt.interval_file_names.size()
												% opt.interval_type_names.size()).str());

	opt.verbose() << "\n\n";
	return true;
}



